#if UNITY_EDITOR
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using UnityEditor;
using UnityEditor.Animations;
using UnityEditor.IMGUI.Controls;
using UnityEngine;
using Object = UnityEngine.Object;
using static GameDesigner.StateMachineStyles;
using Net.Helper;

namespace GameDesigner
{
    public abstract partial class StateMachineViewEditor : Editor
    {
        protected virtual StateMachineView Self { get; set; }
        public static string createScriptName = "NewStateBehaviour";
        public static string stateActionScriptPath = "/Actions/StateActions";
        public static string stateBehaviourScriptPath = "/Actions/StateBehaviours";
        public static string transitionScriptPath = "/Actions/Transitions";
        protected int addBehaviourCode;
        protected float elapsedTime;
        protected float timeElapsed;
        protected static List<Type> stateTypes;
        protected static List<Type> actionTypes;
        protected static List<Type> transitionTypes;
        protected static bool animPlay;
        protected static StateAction playAction;
        protected List<string> clipNames = new();

        protected float DeltaTime
        {
            get
            {
                var deltaTime = (float)(EditorApplication.timeSinceStartup - timeElapsed);
                timeElapsed = (float)EditorApplication.timeSinceStartup;
                return deltaTime;
            }
        }

        protected virtual void OnEnable()
        {
            Self = target as StateMachineView;
            Self.GetClipNames(Self.transform, clipNames);
            if (Self.controller != null) //time状态机初创会报错
                Self.controller.View = Self;
            timeElapsed = (float)EditorApplication.timeSinceStartup;
            if (string.IsNullOrEmpty(Self.editStateMachine.name))
                Self.editStateMachine.name = "Base Layer";
            if (StateMachineWindow.support == null) //这个是假的“null”
                StateMachineWindow.support = null;
            if (StateMachineWindow.support != Self)
                StateMachineWindow.Init(Self);
            if (stateTypes == null)
            {
                stateTypes = new List<Type>();
                AddBehaviourTypes(stateTypes, typeof(StateBehaviour));
            }
            if (actionTypes == null)
            {
                actionTypes = new List<Type>();
                AddBehaviourTypes(actionTypes, typeof(ActionBehaviour));
            }
            if (transitionTypes == null)
            {
                transitionTypes = new List<Type>();
                AddBehaviourTypes(transitionTypes, typeof(TransitionBehaviour));
            }
        }

        protected void AddBehaviourTypes(List<Type> typeList, Type type)
        {
            var assemblies = AppDomain.CurrentDomain.GetAssemblies();
            foreach (var assembly in assemblies)
            {
                var types = assembly.GetTypes().Where(t => t.IsSubclassOf(type)).ToList();
                for (int i = types.Count - 1; i >= 0; i--)
                {
                    if (types[i].GetCustomAttribute<HideBehaviour>(false) != null)
                        types.RemoveAt(i);
                }
                typeList.AddRange(types);
            }
        }

        public override void OnInspectorGUI()
        {
            if (Self == null)
            {
                OpenWindow();
                ResetPropertys();
                return;
            }
            EditorGUI.BeginChangeCheck();
            serializedObject.Update();
            SupportObject.Update();
            OnDrawPreField();
            OpenWindow();
            var sm = Self.editStateMachine;
            if (sm.SelectState != null)
            {
                DrawState(sm.SelectState);
                EditorGUILayout.Space();
                for (int i = 0; i < sm.SelectState.transitions.Length; ++i)
                    DrawTransition(sm.SelectState.transitions[i]);
            }
            else if (StateMachineWindow.selectTransition != null)
                DrawTransition(StateMachineWindow.selectTransition);
            EditorGUILayout.Space();
            if (EditorGUI.EndChangeCheck())
                SetDirty(Self);
            SupportObject.ApplyModifiedProperties();
            serializedObject.ApplyModifiedProperties();
            Repaint();
        }

        protected virtual void OpenWindow()
        {
            if (GUILayout.Button(PluginSetting.Language["Open the state machine editor"], GUI.skin.GetStyle("LargeButtonMid"), GUILayout.ExpandWidth(true)))
            {
                if (Self != null)
                    Self.OnScriptReload();
                StateMachineWindow.ShowWindow(Self);
            }
        }

        protected virtual void OnDrawPreField()
        {
        }

        private SerializedObject _supportObject;
        protected SerializedObject SupportObject => _supportObject ??= new SerializedObject(Self);

        private SerializedProperty _stateMachineObject;
        protected SerializedProperty StateMachineObject => _stateMachineObject ??= SupportObject.FindProperty("editStateMachine");

        private SerializedProperty _statesProperty;
        protected SerializedProperty StatesProperty => _statesProperty ??= StateMachineObject.FindPropertyRelative("states").GetArrayElementAtIndex(Self.editStateMachine.SelectState.Id);

        private SerializedProperty _nameProperty;
        protected SerializedProperty NameProperty => _nameProperty ??= StatesProperty.FindPropertyRelative("name");

        private SerializedProperty _actionSystemProperty;
        protected SerializedProperty ActionSystemProperty => _actionSystemProperty ??= StatesProperty.FindPropertyRelative("actionSystem");

        private SerializedProperty _animSpeedProperty;
        protected SerializedProperty animSpeedProperty => _animSpeedProperty ??= StatesProperty.FindPropertyRelative("animSpeed");

        private SerializedProperty _animLoopProperty;
        protected SerializedProperty animLoopProperty => _animLoopProperty ??= StatesProperty.FindPropertyRelative("animLoop");

        private SerializedProperty _actionsProperty;
        protected SerializedProperty actionsProperty => _actionsProperty ??= StatesProperty.FindPropertyRelative("actions");

        private static State CurrentState;

        protected virtual void ResetPropertys()
        {
            _supportObject = null;
            _stateMachineObject = null;
            _statesProperty = null;
            _nameProperty = null;
            _actionSystemProperty = null;
            _animSpeedProperty = null;
            _animLoopProperty = null;
            _actionsProperty = null;
        }

        protected virtual void OnDrawAnimationField() { }

        /// <summary>
        /// 是否是混合树
        /// </summary>
        /// <param name="animator"></param>
        /// <param name="layer"></param>
        /// <returns></returns>
        public static bool IsStateBlendTree(Animator animator, string clipName, out BlendTree blendTree)
        {
            blendTree = null;
            if (animator == null)
                return false;
            var controller = animator.runtimeAnimatorController as AnimatorController;
            if (controller == null) return false;
            int hash = Animator.StringToHash(clipName);

            foreach (var item in controller.layers)
            {
                var stateMachine = item.stateMachine;

                foreach (var state in stateMachine.states)
                {
                    if (state.state.motion is BlendTree tree && state.state.nameHash == hash)
                    {
                        blendTree = tree;
                        return true;
                    }
                }
            }

            return false;
        }
        /// <summary>
        /// 绘制状态监视面板属性
        /// </summary>
        public void DrawState(State state)
        {
            if (CurrentState != state)
            {
                CurrentState = state;
                ResetPropertys();
            }
            PluginGUILayout.BeginStyleVertical(stateAttributeLabel, dragtabdropwindow, progressBarBack);
            EditorGUILayout.PropertyField(NameProperty, stateNameLabel);
            EditorGUILayout.IntField(stateIdLabel, state.Id);
            if (state.Type == StateType.SubStateMachine)
            {
                EditorGUILayout.HelpBox("你在此状态连线到其他状态，当子状态机执行返回后，会进入到连线的状态。", MessageType.Info);
                EditorGUILayout.EndVertical();
                goto J;
            }
            if (state.Type == StateType.Parent)
            {
                EditorGUILayout.HelpBox("你在此状态连线到其他状态，当返回到父状态机执行后，会进入到连线的状态。", MessageType.Info);
                EditorGUILayout.EndVertical();
                goto J;
            }
            EditorGUILayout.PropertyField(ActionSystemProperty, actionSystemLabel);
            if (state.actionSystem)
                DrawActionPropertyField(state);
            EditorGUILayout.Space();
            DrawBehaviours(state);
            EditorGUILayout.Space();
            EditorGUILayout.EndVertical();
        J:;
        }

        private void DrawActionPropertyField(State state)
        {
            OnDrawAnimationField();
            state.animPlayMode = (AnimPlayMode)EditorGUILayout.Popup(animPlayModeLabel, (int)state.animPlayMode, animPlayModeOptions);
            EditorGUILayout.PropertyField(animSpeedProperty, animSpeedLabel, true);
            EditorGUILayout.PropertyField(animLoopProperty, animLoopLabel, true);
            state.isCrossFade = EditorGUILayout.Toggle(isCrossFadeLabel, state.isCrossFade);
            if (state.isCrossFade)
                state.duration = EditorGUILayout.FloatField(durationLabel, state.duration);
            state.isExitState = EditorGUILayout.Toggle(isExitStateLabel, state.isExitState);
            if (state.isExitState)
                state.dstStateID = EditorGUILayout.Popup(dstStateIDLabel, state.dstStateID, Array.ConvertAll(state.transitions.ToArray(), new Converter<Transition, string>(delegate (Transition t) { return t.CurrState.name + " -> " + t.NextState.name + "   ID:" + t.NextState.Id; })));
            Self.animClipReference = EditorGUILayout.ObjectField(animClipReferenceLabel, Self.animClipReference, typeof(IAnimationClipReference), true);
            PluginGUILayout.BeginStyleVertical(actionTreeLabel, dragtabdropwindow, progressBarBack);
            EditorGUI.indentLevel = 1;
            var actRect = EditorGUILayout.GetControlRect();
            state.foldout = EditorGUI.Foldout(new Rect(actRect.position, new Vector2(actRect.size.x - 120f, 15)), state.foldout, PluginSetting.Language["Action Tree Set"], true);
            if (GUI.Button(new Rect(new Vector2(actRect.size.x - 40f, actRect.position.y), new Vector2(60, 16)), addActionLabel))
            {
                ArrayHelper.Add(ref state.actions, new StateAction() { Id = state.Id, stateMachine = Self.editStateMachine, behaviours = new BehaviourBase[0] });
                return;
            }
            if (GUI.Button(new Rect(new Vector2(actRect.size.x - 100, actRect.position.y), new Vector2(60, 16)), removeActionLabel))
            {
                if (state.actions.Length > 1)
                    ArrayHelper.RemoveAt(ref state.actions, state.actions.Length - 1);
                return;
            }
            if (state.foldout)
                DrawActionPropertyElementField(state);
            PluginGUILayout.EndStyleVertical();
        }

        private void DrawActionPropertyElementField(State state)
        {
            EditorGUI.indentLevel = 2;
            for (int x = 0; x < state.actions.Length; ++x)
            {
                var actionProperty = actionsProperty.GetArrayElementAtIndex(x);
                if (actionProperty == null)
                    continue;
                var action = state.actions[x];
                var foldoutRect = EditorGUILayout.GetControlRect();
                action.foldout = EditorGUI.Foldout(foldoutRect, action.foldout, new GUIContent(PluginSetting.Language["Action ->"] + x, "actions[" + x + "]"), true);
                if (foldoutRect.Contains(Event.current.mousePosition) & Event.current.button == 1)
                {
                    var menu = new GenericMenu();
                    menu.AddItem(removeActionLabel, false, (obj) =>
                    {
                        ArrayHelper.RemoveAt(ref state.actions, (int)obj);
                    }, x);
                    menu.AddItem(copyActionLabel, false, (obj) =>
                    {
                        StateSystem.Component = state.actions[(int)obj];
                    }, x);
                    menu.AddItem(pasteNewActionLabel, StateSystem.CopyComponent != null, () =>
                    {
                        if (StateSystem.Component is StateAction stateAction)
                            ArrayHelper.Add(ref state.actions, CloneHelper.DeepCopy<StateAction>(stateAction, new List<Type>() { typeof(Object), typeof(StateMachine) }));
                    });
                    menu.AddItem(pasteActionLabel, StateSystem.CopyComponent != null, (obj) =>
                    {
                        if (StateSystem.Component is StateAction stateAction)
                        {
                            var index = (int)obj;
                            if (stateAction == state.actions[index])//如果要黏贴的动作是复制的动作则返回
                                return;
                            state.actions[index] = CloneHelper.DeepCopy<StateAction>(stateAction, new List<Type>() { typeof(Object), typeof(StateMachine) });
                        }
                    }, x);
                    menu.ShowAsContext();
                }
                if (action.foldout)
                {
                    EditorGUI.indentLevel = 3;
                    OnDrawSelectAnimationClip(action);
                    OnDrawActionPropertyField(action, actionProperty);
                    EditorGUILayout.PropertyField(actionProperty.FindPropertyRelative("animTime"), animTimeLabel);
                    EditorGUILayout.PropertyField(actionProperty.FindPropertyRelative("animTimeMax"), animTimeMaxLabel);
                    if (Self is StateMachineMono mono && IsStateBlendTree(mono.animator, action.clipName, out BlendTree blendTree))
                    {
                        EditorGUILayout.HelpBox(PluginSetting.Language["BlendTree Info"] + blendTree.blendType, MessageType.Info);
                        EditorGUILayout.PropertyField(actionProperty.FindPropertyRelative("blendParameter"), new GUIContent(PluginSetting.Language["Blend Parameter"] + blendTree.blendParameter, "blendParameter"));
                        action.blendParameterName = blendTree.blendParameter;
                        action.blendParameterYName = blendTree.blendParameterY;
                        switch (blendTree.blendType)
                        {
                            case BlendTreeType.Simple1D:
                                //1D不需要参数Y
                                action.blendParameterYName = string.Empty;
                                break;
                            case BlendTreeType.SimpleDirectional2D:
                                EditorGUILayout.PropertyField(actionProperty.FindPropertyRelative("blendParameterY"), new GUIContent(PluginSetting.Language["Blend ParameterY"] + blendTree.blendParameterY, "blendParameterY"));
                                break;
                            case BlendTreeType.FreeformDirectional2D:
                                EditorGUILayout.PropertyField(actionProperty.FindPropertyRelative("blendParameterY"), new GUIContent(PluginSetting.Language["Blend ParameterY"] + blendTree.blendParameterY, "blendParameterY"));
                                break;
                            case BlendTreeType.FreeformCartesian2D:
                                EditorGUILayout.PropertyField(actionProperty.FindPropertyRelative("blendParameterY"), new GUIContent(PluginSetting.Language["Blend ParameterY"] + blendTree.blendParameterY, "blendParameterY"));
                                break;
                        }
                    }
                    else
                    {
                        action.blendParameter = 0;
                        action.blendParameterY = 0;
                        action.blendParameterName = string.Empty;
                        action.blendParameterYName = string.Empty;
                    }
                    for (int i = 0; i < action.behaviours.Length; ++i)
                    {
                        EditorGUILayout.BeginHorizontal();
                        var rect = EditorGUILayout.GetControlRect();
                        action.behaviours[i].show = EditorGUI.Foldout(new Rect(rect.x, rect.y, 50, rect.height), action.behaviours[i].show, GUIContent.none);
                        action.behaviours[i].Active = EditorGUI.ToggleLeft(new Rect(rect.x + 5, rect.y, 70, rect.height), GUIContent.none, action.behaviours[i].Active);
                        EditorGUI.LabelField(new Rect(rect.x + 20, rect.y, rect.width - 15, rect.height), action.behaviours[i].name, boldLabel);
                        if (GUI.Button(new Rect(rect.x + rect.width - 15, rect.y, rect.width, rect.height), GUIContent.none, toggleMixed))
                        {
                            action.behaviours[i].OnDestroy();
                            ArrayHelper.RemoveAt(ref action.behaviours, i);
                            continue;
                        }
                        if (rect.Contains(Event.current.mousePosition) & Event.current.button == 1)
                        {
                            var menu = new GenericMenu();
                            menu.AddItem(removeActionScriptsLabel, false, (obj) =>
                            {
                                var index = (int)obj;
                                action.behaviours[index].OnDestroy();
                                ArrayHelper.RemoveAt(ref action.behaviours, index);
                            }, i);
                            menu.AddItem(copyActionScriptsLabel, false, (obj) =>
                            {
                                var index = (int)obj;
                                StateSystem.CopyComponent = action.behaviours[index];
                            }, i);
                            menu.AddItem(pasteNewActionScriptsLabel, StateSystem.CopyComponent != null, () =>
                            {
                                if (StateSystem.CopyComponent is ActionBehaviour behaviour)
                                {
                                    var ab = (ActionBehaviour)CloneHelper.DeepCopy(behaviour);
                                    ArrayHelper.Add(ref action.behaviours, ab);
                                }
                            });
                            menu.AddItem(pasteActionScriptValuesLabel, StateSystem.CopyComponent != null, (obj) =>
                            {
                                if (StateSystem.CopyComponent is ActionBehaviour behaviour)
                                {
                                    var index = (int)obj;
                                    if (behaviour.name == action.behaviours[index].name)
                                        action.behaviours[index] = (ActionBehaviour)CloneHelper.DeepCopy(StateSystem.CopyComponent);
                                }
                            }, i);
                            menu.AddItem(editActionLabel, false, (obj) =>
                            {
                                var index = (int)obj;
                                var scriptName = action.behaviours[index].name;
                                if (!Net.Helper.ScriptHelper.Cache.TryGetValue(scriptName, out var sequence))
                                    throw new Exception($"请启用调用帮助设置，菜单:GameDesigner/Network/InvokeHelper");
                                UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(sequence.FilePath, sequence.StartLine, 0);
                            }, i);
                            menu.ShowAsContext();
                        }
                        EditorGUILayout.EndHorizontal();
                        if (action.behaviours[i].show)
                        {
                            EditorGUI.indentLevel = 4;
                            action.behaviours[i].OnInspectorGUI(60f, 5, 4);
                            GUILayout.Space(4);
                            GUILayout.Box(string.Empty, PluginSetting.Instance.HorSpaceStyle, GUILayout.Height(1), GUILayout.ExpandWidth(true));
                            GUILayout.Space(4);
                            EditorGUI.indentLevel = 3;
                        }
                    }
                    OnPlayAnimation(action);
                    var aasRect = EditorGUILayout.GetControlRect();
                    aasRect = new Rect(new Vector2(aasRect.x + (aasRect.size.x / 4f), aasRect.y), new Vector2(aasRect.size.x / 2f, 20));
                    if (GUI.Button(aasRect, addActionScriptsLabel))
                    {
                        var dropdown = new AddBehaviourWindow("Action Component", actionTypes, new AdvancedDropdownState());
                        var maximumSize = dropdown.GetType().GetProperty("maximumSize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                        maximumSize?.SetValue(dropdown, new Vector2(aasRect.width, 500f));
                        dropdown.Init(aasRect, (type) =>
                        {
                            if (type != null)
                            {
                                var stb = (ActionBehaviour)Activator.CreateInstance(type);
                                stb.InitMetadatas();
                                stb.Id = state.Id;
                                stb.Action = action;
                                stb.stateMachine = action.stateMachine;
                                ArrayHelper.Add(ref action.behaviours, stb);
                                SetDirty(Self);
                            }
                            else
                            {
                                addBehaviourCode = 2;
                            }
                        });
                    }
                    if (addBehaviourCode == 2)
                    {
                        EditorGUILayout.Space();
                        EditorGUI.indentLevel = 0;
                        EditorGUILayout.LabelField(createScriptPathsLabel);
                        stateActionScriptPath = EditorGUILayout.TextField(stateActionScriptPath);
                        var addRect = EditorGUILayout.GetControlRect();
                        createScriptName = EditorGUI.TextField(new Rect(addRect.position, new Vector2(addRect.size.x - 125f, 18)), createScriptNameLabel, createScriptName);
                        if (GUI.Button(new Rect(new Vector2(addRect.size.x - 100f, addRect.position.y), new Vector2(120, 18)), createActionScriptsLabel))
                        {
                            var text = Resources.Load<TextAsset>("ActionBehaviourScript");
                            var scriptCode = text.text.Split(new string[] { "\r\n" }, 0);
                            scriptCode[7] = scriptCode[7].Replace("ActionBehaviourScript", createScriptName);
                            ScriptTools.CreateScript(Application.dataPath + stateActionScriptPath, createScriptName, scriptCode);
                            addBehaviourCode = 0;
                        }
                        if (GUILayout.Button(cancelLabel))
                            addBehaviourCode = 0;
                    }
                    EditorGUILayout.Space();
                }
                EditorGUI.indentLevel = 2;
            }
        }

        protected virtual void OnPlayAnimation(StateAction action) { }

        protected virtual void OnDrawSelectAnimationClip(StateAction action)
        {
            var scRect = EditorGUILayout.GetControlRect(hasLabel: true, 18f, EditorStyles.popup);
            scRect = EditorGUI.PrefixLabel(scRect, clipNameLabel);
            if (EditorGUI.DropdownButton(scRect, new GUIContent(action.clipName), FocusType.Passive))
            {
                if (Self.animClipReference is IAnimationClipReference animationClipReference)
                    clipNames = animationClipReference.ClipNames;
                var dropdown = new AddPopupWindow("Select Animation Clip", clipNames, new AdvancedDropdownState());
                var maximumSize = dropdown.GetType().GetProperty("maximumSize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                var minimumSize = dropdown.GetType().GetProperty("minimumSize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                maximumSize?.SetValue(dropdown, new Vector2(scRect.width, 500f));
                minimumSize?.SetValue(dropdown, new Vector2(scRect.width, 150f));
                dropdown.Init(scRect, (clipName) =>
                {
                    action.clipName = clipName;
                    action.length = Self.GetAnimClipLength(action);
                    SetDirty(Self);
                });
            }
        }

        protected virtual void OnDrawActionPropertyField(StateAction action, SerializedProperty actionProperty) { }

        /// <summary>
        /// 绘制状态行为
        /// </summary>
        public void DrawBehaviours(State s)
        {
            GUILayout.Space(10);
            GUILayout.Box("", PluginSetting.Instance.HorSpaceStyle, GUILayout.Height(1), GUILayout.ExpandWidth(true));
            GUILayout.Space(5);
            for (int i = 0; i < s.behaviours.Length; ++i)
            {
                EditorGUI.indentLevel = 1;
                EditorGUILayout.BeginHorizontal();
                var rect = EditorGUILayout.GetControlRect();
                s.behaviours[i].show = EditorGUI.Foldout(new Rect(rect.x, rect.y, 20, rect.height), s.behaviours[i].show, GUIContent.none);
                s.behaviours[i].Active = EditorGUI.ToggleLeft(new Rect(rect.x + 5, rect.y, 30, rect.height), GUIContent.none, s.behaviours[i].Active);
                EditorGUI.LabelField(new Rect(rect.x + 20, rect.y, rect.width - 15, rect.height), s.behaviours[i].name, boldLabel);
                if (GUI.Button(new Rect(rect.x + rect.width - 15, rect.y, rect.width, rect.height), GUIContent.none, toggleMixed))
                {
                    s.behaviours[i].OnDestroy();
                    ArrayHelper.RemoveAt(ref s.behaviours, i);
                    continue;
                }
                if (rect.Contains(Event.current.mousePosition) & Event.current.button == 1)
                {
                    var menu = new GenericMenu();
                    menu.AddItem(removeStateScriptsLabel, false, (obj) =>
                    {
                        var index = (int)obj;
                        s.behaviours[index].OnDestroy();
                        ArrayHelper.RemoveAt(ref s.behaviours, index);
                        return;
                    }, i);
                    menu.AddItem(copyStateScriptsLabel, false, (obj) =>
                    {
                        var index = (int)obj;
                        StateSystem.CopyComponent = s.behaviours[index];
                    }, i);
                    menu.AddItem(pasteNewStateScriptsLabel, StateSystem.CopyComponent != null, delegate ()
                    {
                        if (StateSystem.CopyComponent is StateBehaviour behaviour)
                        {
                            var ab = (StateBehaviour)CloneHelper.DeepCopy(behaviour);
                            ArrayHelper.Add(ref s.behaviours, ab);
                        }
                    });
                    menu.AddItem(pasteStateScriptValuesLabel, StateSystem.CopyComponent != null, (obj) =>
                    {
                        if (StateSystem.CopyComponent is StateBehaviour behaviour)
                        {
                            var index = (int)obj;
                            if (behaviour.name == s.behaviours[index].name)
                                s.behaviours[index] = (StateBehaviour)CloneHelper.DeepCopy(behaviour);
                        }
                    }, i);
                    menu.AddItem(editStatusScriptLabel, false, (obj) =>
                    {
                        var index = (int)obj;
                        var scriptName = s.behaviours[index].name;
                        if (!Net.Helper.ScriptHelper.Cache.TryGetValue(scriptName, out var sequence))
                            throw new Exception($"请启用调用帮助设置，菜单:GameDesigner/Network/InvokeHelper");
                        UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(sequence.FilePath, sequence.StartLine, 0);
                    }, i);
                    menu.ShowAsContext();
                }
                EditorGUILayout.EndHorizontal();
                if (s.behaviours[i].show)
                {
                    EditorGUI.indentLevel = 2;
                    s.behaviours[i].OnInspectorGUI();
                    EditorGUI.indentLevel = 1;
                    GUILayout.Space(4);
                    GUILayout.Box(string.Empty, PluginSetting.Instance.HorSpaceStyle, GUILayout.Height(1), GUILayout.ExpandWidth(true));
                }
            }

            var r = EditorGUILayout.GetControlRect();
            var rr = new Rect(new Vector2(r.x + (r.size.x / 4f), r.y), new Vector2(r.size.x / 2f, 20));
            if (GUI.Button(rr, addStateScriptsLabel))
            {
                var dropdown = new AddBehaviourWindow("State Component", stateTypes, new AdvancedDropdownState());
                var maximumSize = dropdown.GetType().GetProperty("maximumSize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                maximumSize?.SetValue(dropdown, new Vector2(rr.width, 500f));
                dropdown.Init(rr, (type) =>
                {
                    if (type != null)
                    {
                        var stb = (StateBehaviour)Activator.CreateInstance(type);
                        stb.InitMetadatas();
                        stb.Id = s.Id;
                        stb.stateMachine = s.stateMachine;
                        ArrayHelper.Add(ref s.behaviours, stb);
                        SetDirty(Self);
                    }
                    else
                    {
                        addBehaviourCode = 1;
                    }
                });
            }
            if (addBehaviourCode == 1)
            {
                EditorGUILayout.Space();
                EditorGUI.indentLevel = 0;
                EditorGUILayout.LabelField(createStateScriptPathLabel);
                stateBehaviourScriptPath = EditorGUILayout.TextField(stateBehaviourScriptPath);
                var addRect = EditorGUILayout.GetControlRect();
                createScriptName = EditorGUI.TextField(new Rect(addRect.position, new Vector2(addRect.size.x - 125f, 18)), createScriptName);
                if (GUI.Button(new Rect(new Vector2(addRect.size.x - 105f, addRect.position.y), new Vector2(120, 18)), createStateScriptLabel))
                {
                    var text = Resources.Load<TextAsset>("StateBehaviourScript");
                    var scriptCode = text.text.Split(new string[] { "\r\n" }, 0);
                    scriptCode[7] = scriptCode[7].Replace("StateBehaviourScript", createScriptName);
                    ScriptTools.CreateScript(Application.dataPath + stateBehaviourScriptPath, createScriptName, scriptCode);
                    addBehaviourCode = 0;
                }
                if (GUILayout.Button(cancelLabel))
                    addBehaviourCode = 0;
            }
        }

        /// <summary>
        /// 绘制状态连接行为
        /// </summary>
        public void DrawTransition(Transition tr)
        {
            EditorGUI.indentLevel = 0;
            var style = GUI.skin.GetStyle("dragtabdropwindow");
            style.fontStyle = FontStyle.Bold;
            style.font = Resources.Load<Font>("Arial");
            style.normal.textColor = Color.red;
            GUILayout.Button(PluginSetting.Language["Connection properties"] + tr.CurrState.name + " -> " + tr.NextState.name, style);
            tr.name = tr.CurrState.name + " -> " + tr.NextState.name;
            EditorGUILayout.BeginVertical(progressBarBack);

            EditorGUILayout.Space();

            tr.mode = (TransitionMode)EditorGUILayout.Popup(PluginSetting.Language["Connection mode"], (int)tr.mode, Enum.GetNames(typeof(TransitionMode)), preDropDown);
            switch (tr.mode)
            {
                case TransitionMode.ExitTime:
                    tr.time = EditorGUILayout.FloatField(PluginSetting.Language["current time"], tr.time, preDropDown);
                    tr.exitTime = EditorGUILayout.FloatField(PluginSetting.Language["End time"], tr.exitTime, preDropDown);
                    EditorGUILayout.HelpBox(PluginSetting.Language["The current time will automatically enter the next state at the end of the time."], MessageType.Info);
                    break;
            }

            GUILayout.Space(10);
            GUILayout.Box(string.Empty, PluginSetting.Instance.HorSpaceStyle, GUILayout.Height(1), GUILayout.ExpandWidth(true));
            GUILayout.Space(10);

            tr.isEnterNextState = EditorGUILayout.Toggle(PluginSetting.Language["Enter the next state"], tr.isEnterNextState);

            GUILayout.Space(10);
            GUILayout.Box(string.Empty, PluginSetting.Instance.HorSpaceStyle, GUILayout.Height(1), GUILayout.ExpandWidth(true));

            for (int i = 0; i < tr.behaviours.Length; ++i)
            {
                if (tr.behaviours[i] == null)
                {
                    ArrayHelper.RemoveAt(ref tr.behaviours, i);
                    continue;
                }
                EditorGUI.indentLevel = 1;
                EditorGUILayout.BeginHorizontal();
                var rect = EditorGUILayout.GetControlRect();
                EditorGUI.LabelField(new Rect(rect.x + 20, rect.y, rect.width - 15, 20), tr.behaviours[i].GetType().Name, boldLabel);
                tr.behaviours[i].show = EditorGUI.Foldout(new Rect(rect.x, rect.y, 20, 20), tr.behaviours[i].show, GUIContent.none, true);
                tr.behaviours[i].Active = EditorGUI.ToggleLeft(new Rect(rect.x + 5, rect.y, 30, 20), GUIContent.none, tr.behaviours[i].Active);
                if (GUI.Button(new Rect(rect.x + rect.width - 15, rect.y, rect.width, rect.height), GUIContent.none, toggleMixed))
                {
                    tr.behaviours[i].OnDestroy();
                    ArrayHelper.RemoveAt(ref tr.behaviours, i);
                    continue;
                }
                if (rect.Contains(Event.current.mousePosition) & Event.current.button == 1)
                {
                    var menu = new GenericMenu();
                    menu.AddItem(removeTransisionScripsLabel, false, (obj) =>
                    {
                        var index = (int)obj;
                        tr.behaviours[index].OnDestroy();
                        ArrayHelper.RemoveAt(ref tr.behaviours, index);
                        return;
                    }, i);
                    menu.AddItem(copyTransisionScripsLabel, false, (obj) =>
                    {
                        var index = (int)obj;
                        StateSystem.CopyComponent = tr.behaviours[index];
                    }, i);
                    menu.AddItem(pasteNewTransisionScripsLabel, StateSystem.CopyComponent != null, () =>
                    {
                        if (StateSystem.CopyComponent is TransitionBehaviour behaviour)
                        {
                            var ab = (TransitionBehaviour)CloneHelper.DeepCopy(behaviour);
                            ArrayHelper.Add(ref tr.behaviours, ab);
                        }
                    });
                    menu.AddItem(pasteTransisionScripsValuesLabel, StateSystem.CopyComponent != null, (obj) =>
                    {
                        var index = (int)obj;
                        if (StateSystem.CopyComponent is TransitionBehaviour behaviour)
                            if (behaviour.name == tr.behaviours[index].name)
                                tr.behaviours[index] = (TransitionBehaviour)CloneHelper.DeepCopy(behaviour);
                    }, i);
                    menu.AddItem(editTransisionScripsLabel, false, (obj) =>
                    {
                        var index = (int)obj;
                        var scriptName = tr.behaviours[index].name;
                        if (!Net.Helper.ScriptHelper.Cache.TryGetValue(scriptName, out var sequence))
                            throw new Exception($"请启用调用帮助设置，菜单:GameDesigner/Network/InvokeHelper");
                        UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(sequence.FilePath, sequence.StartLine, 0);
                    }, i);
                    menu.ShowAsContext();
                }
                EditorGUILayout.EndHorizontal();
                if (tr.behaviours[i].show)
                {
                    EditorGUI.indentLevel = 2;
                    tr.behaviours[i].OnInspectorGUI();
                    EditorGUI.indentLevel = 1;
                    GUILayout.Space(10);
                    GUILayout.Box(string.Empty, PluginSetting.Instance.HorSpaceStyle, GUILayout.Height(1), GUILayout.ExpandWidth(true));
                }
            }

            GUILayout.Space(5);

            var atRect = EditorGUILayout.GetControlRect();
            var atsRect = new Rect(new Vector2(atRect.x + (atRect.size.x / 4f), atRect.y), new Vector2(atRect.size.x / 2f, 20));
            if (GUI.Button(atsRect, addTransisionScripsLabel))
            {
                var dropdown = new AddBehaviourWindow("Transition Component", transitionTypes, new AdvancedDropdownState());
                var maximumSize = dropdown.GetType().GetProperty("maximumSize", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public);
                maximumSize?.SetValue(dropdown, new Vector2(atsRect.width, 500f));
                dropdown.Init(atsRect, (type) =>
                {
                    if (type != null)
                    {
                        var stb = (TransitionBehaviour)Activator.CreateInstance(type);
                        stb.InitMetadatas();
                        stb.Id = tr.CurrState.Id;
                        stb.Transition = tr;
                        stb.stateMachine = tr.stateMachine;
                        ArrayHelper.Add(ref tr.behaviours, stb);
                    }
                    else
                    {
                        addBehaviourCode = 3;
                    }
                });
            }
            if (addBehaviourCode == 3)
            {
                EditorGUILayout.Space();
                EditorGUILayout.LabelField(createTransisionScripsPathLabel);
                transitionScriptPath = EditorGUILayout.TextField(transitionScriptPath);
                var addRect = EditorGUILayout.GetControlRect();
                createScriptName = EditorGUI.TextField(new Rect(addRect.position, new Vector2(addRect.size.x - 125f, 18)), createScriptName);
                if (GUI.Button(new Rect(new Vector2(addRect.size.x - 105f, addRect.position.y), new Vector2(120, 18)), createTransisionScripsLabel))
                {
                    var text = Resources.Load<TextAsset>("TransitionBehaviorScript");
                    var scriptCode = text.text.Split(new string[] { "\r\n" }, 0);
                    scriptCode[7] = scriptCode[7].Replace("TransitionBehaviorScript", createScriptName);
                    ScriptTools.CreateScript(Application.dataPath + transitionScriptPath, createScriptName, scriptCode);
                    addBehaviourCode = 0;
                }
                if (GUILayout.Button(cancelLabel))
                    addBehaviourCode = 0;
            }
            GUILayout.Space(10);
            EditorGUILayout.HelpBox(PluginSetting.Language["You can create a connection behavior script to control the state to the next state"], MessageType.Info);
            GUILayout.Space(10);
            EditorGUILayout.EndHorizontal();
        }

        [UnityEditor.Callbacks.DidReloadScripts(0)]
        internal static void OnScriptReload()
        {
            var go = Selection.activeGameObject;
            if (go == null)
                return;
            if (!go.TryGetComponent<StateManager>(out var sm))
                return;
            if (sm.support == null)
                return;
            sm.support.OnScriptReload();
        }

        protected void SetDirty(Object target)
        {
            EditorUtility.SetDirty(target);
        }
    }
}
#endif